package com.waqu.common.utils.io;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.lang3.ClassUtils;
import org.springframework.asm.ClassReader;

import com.waqu.common.utils.collection.ArrayMapTools;
import com.waqu.common.utils.text.StringTools;

/**
 * @Title ClassTools.java
 * @Package com.waqu.common.utils.io
 * @author tangjunfeng
 * @date 2017年8月21日 下午4:03:46
 * @version V1.0
 */
public class ClassTools {

	private static final String CLASS_SUFFIX = ".class";
	private static final String JAR_PROTOCOL = "jar";

	/** 获取默认的类加载器 */
	public static ClassLoader getDefaultClassLoader() {
		try {
			return Thread.currentThread().getContextClassLoader();
		} catch (Throwable ex) {
			return ClassUtils.class.getClassLoader();
		}
	}

	/** 获取资源对象 */
	public static List<URL> findResourcesForPackage(String... packages) throws IOException {
		if (ArrayMapTools.isEmpty(packages))
			return new ArrayList<>();
		Set<URL> urls = new HashSet<>();
		for (String _pack : packages) {
			// 转换格式为：com/xx
			urls.addAll(findResourcesForUrl(_pack.replace(".", "/")));
		}
		return new ArrayList<>(urls);
	}

	/** 获取资源对象，单一路径, com/waqu */
	public static List<URL> findResourcesForUrl(String location) throws IOException {
		if (location.startsWith("/")) // 删除前缀中的/
			location = location.substring(1);
		if (!location.endsWith("/")) // 尾部添加/ 否则无法正常拼接className
			location += "/";
		// 获取当前classLoader中包含该前缀的 class或jar
		Enumeration<URL> resourceUrls = getDefaultClassLoader().getResources(location);
		List<URL> result = new ArrayList<>();
		while (resourceUrls.hasMoreElements()) {
			result.add(resourceUrls.nextElement());
		}
		return result;
	}

	/**
	 * 获取指定路径下的指定文件列表
	 * 
	 * @throws URISyntaxException
	 * @throws MalformedURLException
	 */
	public static List<URL> getClassUrlsForFiles(URL rootFileUrl) throws URISyntaxException, MalformedURLException {
		if (null == rootFileUrl)
			return new ArrayList<>();
		File rootFile = new File(rootFileUrl.toURI());
		List<URL> fileList = new ArrayList<>();
		if (rootFile.isFile() && rootFile.getName().endsWith(CLASS_SUFFIX)) {
			fileList.add(rootFile.toURI().toURL());
			return fileList;
		}
		if (!rootFile.isDirectory())
			return fileList;
		for (File file : rootFile.listFiles()) {
			if (file.isDirectory()) {
				fileList.addAll(getClassUrlsForFiles(file.toURI().toURL()));
				continue;
			}
			if (file.isFile() && file.getName().endsWith(CLASS_SUFFIX))
				fileList.add(file.toURI().toURL());
		}
		return fileList;
	}

	public static List<URL> getClassUrlsForJar(URL jarUrl) throws IOException, ClassNotFoundException {
		if (null == jarUrl || !JAR_PROTOCOL.equals(jarUrl.getProtocol()))
			return new ArrayList<>();
		List<URL> result = new ArrayList<>();
		// 打开jar文件
		URLConnection con = jarUrl.openConnection();
		JarURLConnection jarCon = (JarURLConnection) con;
		jarCon.setUseCaches(false);
		JarFile jarFile = jarCon.getJarFile();
		JarEntry jarEntry = jarCon.getJarEntry();
		// 是否有搜索条件
		String rootEntryPath = StringTools.ternary(jarEntry != null, jarEntry.getName(), "");
		// 添加搜索下一级
		if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/"))
			rootEntryPath = rootEntryPath + "/";
		// 循环获取jar中的资源
		for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
			JarEntry entry = entries.nextElement();
			String entryPath = entry.getName();
			// 只获取指定目录下的class资源
			if (entryPath.startsWith(rootEntryPath) && entryPath.endsWith(CLASS_SUFFIX)) {
				// 因jar文件带有搜索结果，所以在读取到的资源中去除搜索结果前缀，才能创建出正常的url
				result.add(createRelative(jarUrl, entryPath.substring(rootEntryPath.length())));
			}
		}
		return result;

	}

	private static URL createRelative(URL url, String relativePath) throws MalformedURLException {
		if (relativePath.startsWith("/")) {
			relativePath = relativePath.substring(1);
		}
		return new URL(url, relativePath);
	}

	public static String getClassName(URL url) throws IOException {
		if (null == url)
			return null;
		InputStream is = url.openConnection().getInputStream();
		ClassReader classReader = new ClassReader(is);
		return classReader.getClassName().replace("/", ".");
	}

	public static Class<?> getClass(URL url) throws IOException, ClassNotFoundException {
		return getDefaultClassLoader().loadClass(getClassName(url));
	}

	public static boolean isWithAnnotation(URL url, Class<? extends Annotation> annotationClass)
			throws ClassNotFoundException, IOException {
		return getClass(url).isAnnotationPresent(annotationClass);
	}

	public static <T extends Annotation> T getAnnotation(URL url, Class<T> t)
			throws ClassNotFoundException, IOException {
		return getClass(url).getAnnotation(t);
	}

}
